package com.onesports.intelligent.k12.polarlight.service.course;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.onesports.framework.kit.common.util.BeanUtils;
import com.onesports.framework.kit.common.util.ParamUtils;
import com.onesports.framework.kit.common.util.TimeUtils;
import com.onesports.intelligent.k12.polarlight.common.domain.entity.BaseEntity;
import com.onesports.intelligent.k12.polarlight.common.errors.exceptions.BusinessException;
import com.onesports.intelligent.k12.polarlight.common.utils.Assert;
import com.onesports.intelligent.k12.polarlight.domain.dto.course.AdjustPriceDTO;
import com.onesports.intelligent.k12.polarlight.domain.dto.course.CourseManegeSaveOrUpdateDTO;
import com.onesports.intelligent.k12.polarlight.domain.dto.course.CourseOpenDTO;
import com.onesports.intelligent.k12.polarlight.domain.dto.course.CourseQueryDTO;
import com.onesports.intelligent.k12.polarlight.domain.entity.course.*;
import com.onesports.intelligent.k12.polarlight.domain.entity.market.Product;
import com.onesports.intelligent.k12.polarlight.domain.entity.organization.OrganizationCampus;
import com.onesports.intelligent.k12.polarlight.domain.vo.common.CanChoiceDataVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.*;
import com.onesports.intelligent.k12.polarlight.domain.vo.student.StudentCourseVO;
import com.onesports.intelligent.k12.polarlight.enums.AuditStatusEnum;
import com.onesports.intelligent.k12.polarlight.enums.ClassInfoStatusEnum;
import com.onesports.intelligent.k12.polarlight.mapper.course.CourseMapper;
import com.onesports.intelligent.k12.polarlight.mapper.course.CourseScheduleMapper;
import com.onesports.intelligent.k12.polarlight.security.SecurityUtils;
import com.onesports.intelligent.k12.polarlight.service.campus.OrganizationCampusService;
import com.onesports.intelligent.k12.polarlight.service.common.CommonService;
import com.onesports.intelligent.k12.polarlight.service.course.manage.CourseGuidePriceRelService;
import com.onesports.intelligent.k12.polarlight.service.course.manage.CourseOpenService;
import com.onesports.intelligent.k12.polarlight.service.order.OrderService;
import com.onesports.intelligent.k12.polarlight.service.student.ClassStudentService;
import com.onesports.intelligent.k12.polarlight.service.sys.ProductService;
import com.onesports.intelligent.k12.polarlight.util.BeanCopyUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTabJc;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Wsy
 **/
@Service
@RequiredArgsConstructor
public class CourseService extends ServiceImpl<CourseMapper, Course> {

    private final CourseMapper courseMapper;
    private final CourseGuidePriceRelService guidePriceRelService;
    private final CourseSellPriceRelService courseSellPriceRelService;
    private final AdjustPriceRecordService adjustPriceRecordService;
    private final CourseOpenService courseOpenService;
    private final ProductService productService;
    private final CourseScheduleService scheduleService;
    private final OrganizationCampusService organizationCampusService;
    private final OrderService orderService;
    private final CommonService commonService;
    private final CourseScheduleMapper courseScheduleMapper;
    private final ClassStudentService classStudentService;
    private final CourseScheduleDetailService courseScheduleDetailService;

    private static final String WEEK_PID = "5953d34f136a1e290b27650a28376f95";

    /**
     * 课程下拉列表
     *
     * @param courseName 课程名称
     * @return List<CanChoiceDataVO>
     */
    public List<CanChoiceDataVO> dropDown(String courseName) {
        List<Course> list = list(new LambdaQueryWrapper<Course>()
                .like(StringUtils.isNotEmpty(courseName), Course::getCourseName, courseName))
                .stream()
                .distinct()
                .collect(Collectors.toList());
        if (ParamUtils.isEmpty(list)) {
            return new ArrayList<>();
        }

        //获取课程开设时间数据
        List<String> courseIds = list.stream().map(BaseEntity::getId).collect(Collectors.toList());
        List<CourseOpen> courseOpens = this.courseOpenService.listByCourseIdsAndOrganizationId(courseIds, SecurityUtils.getOrganizationId());
        if (ParamUtils.isEmpty(courseOpens)) {
            return list.stream().map(i -> {
                CanChoiceDataVO canChoiceData = BeanUtils.copyProperties(i, CanChoiceDataVO.class);
                canChoiceData.setName(i.getCourseName());
                return canChoiceData;
            }).collect(Collectors.toList());
        } else {
            Map<String, List<CourseOpen>> openMap = courseOpens.stream().collect(Collectors.groupingBy(CourseOpen::getCourseId));
            return list.stream().map(i -> {
                CanChoiceDataVO canChoiceData = BeanUtils.copyProperties(i, CanChoiceDataVO.class);
                canChoiceData.setName(i.getCourseName());
                canChoiceData.setIsSetOpenTime(ParamUtils.isNotEmpty(openMap.get(i.getId())));
                return canChoiceData;
            }).collect(Collectors.toList());
        }

    }


    /**
     * 课程下拉列表(有设置开设时间的课程)
     *
     * @param courseName     课程名称
     * @param organizationId 机构id
     * @return List<CanChoiceDataVO>
     */
    public List<CanChoiceDataVO> dropDown(String courseName, String organizationId) {

        //获取机构下课程的开设时间
        List<CourseOpen> courseOpens = this.courseOpenService.list(new LambdaQueryWrapper<CourseOpen>()
                .eq(CourseOpen::getOrganizationId, organizationId));
        if (ParamUtils.isEmpty(courseOpens)) {
            return new ArrayList<>();
        }

        List<String> courseIds = courseOpens.stream().map(CourseOpen::getCourseId).distinct().collect(Collectors.toList());
        List<Course> list = list(new LambdaQueryWrapper<Course>()
                .like(StringUtils.isNotEmpty(courseName), Course::getCourseName, courseName)
                .in(BaseEntity::getId, courseIds))
                .stream()
                .distinct()
                .collect(Collectors.toList());

        if (ParamUtils.isEmpty(list)) {
            return new ArrayList<>();
        }

        return list.stream().map(i -> CanChoiceDataVO.builder().id(i.getId())
                .name(i.getCourseName()).build()).collect(Collectors.toList());
    }


    /**
     * 课程分页
     *
     * @param page 分页参数
     * @param dto  入参
     * @return IPage<CourseVO>
     */
    public IPage<CourseVO> coursePage(Page<CourseVO> page, CourseQueryDTO dto) {
        IPage<CourseVO> coursePage = this.courseMapper.coursePage(page, dto);
        List<CourseVO> records = coursePage.getRecords();
        if (ParamUtils.isNotEmpty(records)) {
            List<String> courseIds = records.stream().map(CourseVO::getCourseId).collect(Collectors.toList());
            List<CourseGuidePriceRel> relList = this.guidePriceRelService.selectByCourseIds(courseIds);
            if (ParamUtils.isNotEmpty(relList)) {
                Map<String, List<CourseGuidePriceRelVO>> relMap = BeanCopyUtil.copyListProperties(relList, CourseGuidePriceRelVO::new).stream().collect(Collectors.groupingBy(CourseGuidePriceRelVO::getCourseId));
                records.forEach(i -> i.setGuidePriceRel(relMap.get(i.getCourseId())));
                coursePage.setRecords(records);
            }
        }
        return coursePage;
    }

    /**
     * 课程社区售价详情
     *
     * @param page     分页参数
     * @param courseId 课程id
     * @return IPage<CourseSellPriceRelVO>
     */
    public IPage<CourseSellPriceRelVO> sellPriceDetail(Page<CourseSellPriceRelVO> page, String courseId) {
        return this.courseSellPriceRelService.sellPricePage(page, courseId);
    }

    /**
     * 申请调价
     *
     * @param dto 入参
     */
    @Transactional(rollbackFor = Exception.class)
    public void applyAdjustPrice(AdjustPriceDTO dto) {

        //校验是否可调
        this.checkAdjustPrice(dto.getCourseId(), dto.getCampusId());

        //校验是否重复提交
        this.checkRepeatSubmit(dto);
        Date current = new Date();
        //生成调价记录
        CourseAdjustPriceRecord adjustPriceRecord = BeanUtils.copyProperties(dto, CourseAdjustPriceRecord.class);

        adjustPriceRecord.setApplyTime(current);
        adjustPriceRecord.setApplyBy(SecurityUtils.getNickname());

        //获取指导价
        CourseGuidePriceRel guidePriceRel = this.guidePriceRelService.getById(dto.getGuidePriceId());
        adjustPriceRecord.setGuidePrice(guidePriceRel.getPrice());

        //获取课程售价id
        CourseSellPriceRel courseSellPriceRel = this.courseSellPriceRelService.getByCourseIdAndCampusId(dto.getCourseId(), dto.getCampusId(), guidePriceRel.getId());
        if (courseSellPriceRel == null) {
            courseSellPriceRel = new CourseSellPriceRel();
            this.initCourseSellPrice(courseSellPriceRel, guidePriceRel, dto.getCampusId(), dto.getOrganizationId());
        }
        adjustPriceRecord.setCourseSellPriceRelId(courseSellPriceRel.getId());
        if (!this.adjustPriceRecordService.save(adjustPriceRecord)) {
            throw new BusinessException("提交异常");
        }
    }

    private void checkRepeatSubmit(AdjustPriceDTO dto) {
        //同一校区同一课程同一类型存在待审核的记录则提示
        List<CourseAdjustPriceRecord> list = this.adjustPriceRecordService.list(new LambdaQueryWrapper<CourseAdjustPriceRecord>()
                .eq(CourseAdjustPriceRecord::getCampusId, dto.getCampusId())
                .eq(CourseAdjustPriceRecord::getCourseId, dto.getCourseId())
                .eq(CourseAdjustPriceRecord::getGuidePriceId, dto.getGuidePriceId())
                .eq(CourseAdjustPriceRecord::getAuditStatus, AuditStatusEnum.WAIT.getValue()));
        if (ParamUtils.isNotEmpty(list)) {
            throw new BusinessException("已提交成功，请勿重复提交");
        }
    }

    private void checkAdjustPrice(String courseId, String campusId) {
        List<Product> products = this.productService.getSellProduct(courseId, campusId);
        if (ParamUtils.isNotEmpty(products)) {
            throw new BusinessException("本课程已上架售卖，如需申请调价，请先下架商品。待调价完成后重新上架商品。");
        }
    }

    /**
     * 根据课程ID获取拼版规则集合
     *
     * @param ids
     * @return
     */
    public List<CourseGuidePriceRelVO> queryClassMergeRuleByIds(List<String> ids) {
        return courseMapper.queryClassMergeRuleByIds(ids);
    }

    /**
     * 判断ID是否存在，不存在制空并且返回
     *
     * @param course
     * @return
     */
    public CourseManegeSaveOrUpdateDTO checkId(CourseManegeSaveOrUpdateDTO course) {
        if (StringUtils.isEmpty(course.getId())) {
            return course;
        }
        Course entity = getById(course.getId());
        if (entity != null) {
            return course;
        } else {
            throw new BusinessException("修改的用户不存在");
        }
    }

    /**
     * 获取code集合
     *
     * @return
     */
    public List<String> queryCodeList() {
        return list().stream().map(Course::getCourseCode).distinct().collect(Collectors.toList());
    }

    /**
     * 检查名称
     *
     * @param course
     * @return 没有重复返回true
     */
    public Boolean checkName(Course course) {
        //查询出所有的名称对应的集合
        Map<String, List<Course>> nameMap = list().stream().collect(Collectors.groupingBy(Course::getCourseName));
        if (nameMap.size() == 0 || MapUtils.isEmpty(nameMap)) {
            return true;
        }

        if (StringUtils.isEmpty(course.getId())) {
            //执行新增操作，一个都不允许重复
            List<String> nameList = nameMap.keySet().stream().filter(i -> course.getCourseName().equals(i)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(nameList)) {
                return true;
            }
            return false;
        }
        if (CollectionUtils.isEmpty(nameMap.get(course.getCourseName()))) {
            return true;
        }
        //有这个名字，但要看ID是否一致，不一致返回false
        List<Course> collect = nameMap.get(course.getCourseName()).stream().filter(i -> course.getId().equals(i.getId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(collect)) {
            return false;
        }
        return true;
    }


    /**
     * 调价记录
     *
     * @param page                分页参数
     * @param applyOrganizationId 申请机构id
     * @param campusId            校区id
     * @param courseId            课程id
     * @param auditStatus         审核状态
     * @param auditBy             审核人
     * @return IPage<CourseAdjustPriceRecordVO>
     */
    public IPage<CourseAdjustPriceRecordVO> adjustRecord(Page<Object> page, String applyOrganizationId,
                                                         String campusId, String courseId, String auditStatus, String auditBy, String typeName) {
        return this.adjustPriceRecordService.adjustRecord(page, applyOrganizationId, campusId, courseId, auditStatus, auditBy, typeName);
    }


    /**
     * 设置课程开设时间
     *
     * @param dto 入参
     */
    @Transactional(rollbackFor = Exception.class)
    public void setOpenTime(CourseOpenDTO dto) {
        // 删除原课程开设时间
        this.courseOpenService.remove(new LambdaQueryWrapper<CourseOpen>()
                .eq(CourseOpen::getCourseId, dto.getCourseId())
                .eq(CourseOpen::getOrganizationId, dto.getOrganizationId()));
        //重新添加
        List<CourseOpen> courseOpens = dto.getList().stream()
                .map(i -> CourseOpen.builder()
                        .courseId(dto.getCourseId())
                        .organizationId(dto.getOrganizationId())
                        .scheduleTemplateId(i.getId())
                        .sectionCode(i.getSectionCode())
                        .startTime(i.getStartTime())
                        .endTime(i.getEndTime())
                        .build()).collect(Collectors.toList());
        this.courseOpenService.saveBatch(courseOpens);

        //获取最新的开设时间
        List<CourseOpenVO> courseOpenList = this.courseOpenService.listByCourseIdAndOrganizationId(dto.getCourseId(), dto.getOrganizationId());

        //更新未发布的课表时段数据
        this.scheduleService.updUnPublishCourseSchedule(dto, courseOpenList);

    }


    /**
     * 初始化校区课程售价
     *
     * @param campusId       校区id
     * @param organizationId 当前登录人所属机构
     */
    public void initCourseSellPrice(String campusId, String organizationId) {
        //获取所有课程指导价
        List<CourseGuidePriceRel> list = this.guidePriceRelService.list();
        if (ParamUtils.isNotEmpty(list)) {
            this.batchSaveSellPrice(list, campusId, organizationId);
        }
    }


    /**
     * 初始化校区课程售价
     *
     * @param campusId       校区id
     * @param sellPriceRel   售价
     * @param guidePriceRel  指导价
     * @param organizationId 机构id
     */
    public void initCourseSellPrice(CourseSellPriceRel sellPriceRel, CourseGuidePriceRel guidePriceRel, String campusId, String organizationId) {
        BeanUtils.copyProperties(guidePriceRel, sellPriceRel);
        sellPriceRel.setGuidePriceId(guidePriceRel.getId());
        sellPriceRel.setCampusId(campusId);
        sellPriceRel.setOrganizationId(organizationId);
        this.courseSellPriceRelService.save(sellPriceRel);
    }


    /**
     * 获取已设置的课程开设时间
     *
     * @param courseId       课程id
     * @param organizationId 当前登录人所属机构
     * @return List<CourseOpenVO>
     */
    public List<CourseOpenVO> courseOpen(String courseId, String organizationId) {

        List<CourseOpen> list = this.courseOpenService.list(new LambdaQueryWrapper<CourseOpen>()
                .eq(CourseOpen::getCourseId, courseId)
                .eq(CourseOpen::getOrganizationId, organizationId)
                .eq(BaseEntity::getDeleted, false));

        if (ParamUtils.isEmpty(list)) {
            return new ArrayList<>();
        }

        return BeanCopyUtil.copyListProperties(list, CourseOpenVO::new);
    }


    /**
     * 初始化课程校区售价
     *
     * @param courseId 课程id
     */
    public void initCourseSellPrice(String courseId, List<CourseGuidePriceRel> courseGuidePriceRelList) {
        //校验参数是否符合条件
        Course course = this.getById(courseId);
        Assert.notNull(course, "课程不存在");

        if (ParamUtils.isNotEmpty(courseGuidePriceRelList)) {
            //获取所有校区
            List<OrganizationCampus> list = this.organizationCampusService.list();
            if (ParamUtils.isNotEmpty(list)) {
                //获取当前课程的指导价
                if (ParamUtils.isNotEmpty(courseGuidePriceRelList)) {
                    list.forEach(i -> this.batchSaveSellPrice(courseGuidePriceRelList, i.getId(), i.getOrganizationId()));
                }
            }
        }

    }

    private void batchSaveSellPrice(List<CourseGuidePriceRel> courseGuidePriceRelList, String campusId, String organizationId) {
        List<CourseSellPriceRel> sellPriceRel = courseGuidePriceRelList.stream()
                .map(c -> CourseSellPriceRel.builder()
                        .courseId(c.getCourseId())
                        .guidePriceId(c.getId())
                        .price(c.getPrice())
                        .campusId(campusId)
                        .organizationId(organizationId)
                        .build()).collect(Collectors.toList());
        this.courseSellPriceRelService.saveBatch(sellPriceRel);
    }


    /**
     * 课程列表
     *
     * @param studentId 学员id
     * @param campusIds  校区id 学员管理那边调用的时候才要传
     * @return List<StudentCourseVO>
     */
    public List<StudentCourseVO> courseList(String studentId, List<String> campusIds) {
        List<StudentCourseVO> courseList = this.orderService.courseList(studentId, campusIds, Arrays.asList(ClassInfoStatusEnum.IN_CLASS.getValue(), ClassInfoStatusEnum.FAIL_CLASS.getValue()));
        if (ParamUtils.isNotEmpty(courseList)) {
            //获取课程
            Map<String, String> courseMap = this.commonService.courseMap();
            //获取教练
            Map<String, String> coachMap = this.commonService.coachMap();
            //获取星期
            Map<String, String> weekMap = this.commonService.weekMap(WEEK_PID);
            //获取学员课程各个课次统计
            List<String> classIds = courseList.stream().map(StudentCourseVO::getClassId).distinct().collect(Collectors.toList());
            //获取课表id
            List<String> scheduleIds = courseList.stream().map(StudentCourseVO::getScheduleId).collect(Collectors.toList());
            List<CourseScheduleDetail> details = this.courseScheduleDetailService.listByScheduleIds(scheduleIds);
            Map<String, List<CourseScheduleDetail>> detailMap = details.stream().collect(Collectors.groupingBy(CourseScheduleDetail::getScheduleId));
            List<ClassStudent> classStudents = this.classStudentService.list(new LambdaQueryWrapper<ClassStudent>()
                    .eq(ClassStudent::getStudentId, studentId)
                    .in(ClassStudent::getClassInfoId, classIds)
                    .eq(ClassStudent::getActive, true));
            Map<String, ClassStudent> stuMap = new HashMap<>();
            if (ParamUtils.isNotEmpty(classStudents)) {
                stuMap = classStudents.stream().collect(Collectors.toMap(ClassStudent::getClassInfoId, stu -> stu));
            }
            Map<String, ClassStudent> unChangeStuMap = stuMap;
            courseList.forEach(i -> {
                String[] weekCode = i.getWeekCode();
                if (ParamUtils.isNotEmpty(weekCode)) {
                    List<String> weekCodes = Arrays.asList(weekCode);
                    weekCodes = weekCodes.stream().map(Integer::parseInt)
                            .collect(Collectors.toList()).stream().sorted(Comparator.comparing(Integer::new))
                            .map(String::valueOf).collect(Collectors.toList());

                    List<String> week = new ArrayList<>();
                    for (String s : weekCodes) {
                        week.add(weekMap.get(s));
                    }

                    i.setWeekCode(week.toArray(new String[0]));
                }
                i.setCoachName(coachMap.get(i.getCoachId()));
                i.setCourseName(courseMap.get(i.getCourseId()));
                List<CourseScheduleDetail> scheduleDetails = detailMap.get(i.getScheduleId());
                if (ParamUtils.isNotEmpty(scheduleDetails)) {
                    i.setCourseDate(scheduleDetails.stream()
                            .sorted(Comparator.comparing(CourseScheduleDetail::getCourseDate))
                            .map(j -> TimeUtils.toText(j.getCourseDate(), TimeUtils.DATE_FORMAT))
                            .distinct().collect(Collectors.toList()));
                }
                ClassStudent classStudent = unChangeStuMap.get(i.getClassId());
                if (classStudent != null) {
                    i.setTotalNum(classStudent.getTotalNum());
                    i.setAbsentNum(classStudent.getAbsentNum());
                    i.setSignUpNum(classStudent.getSignNum());
                    i.setLeaveNum(classStudent.getLeaveNum());
                }
            });
        }
        return courseList;

    }

    /**
     * 根据校区id获取课程下拉
     *
     * @param campusId 校区id
     * @return CampusCourseVO
     */
    public List<CampusCourseVO> courseByCampusId(String campusId) {
        return this.courseMapper.courseByCampusId(campusId);
    }

    /**
     * 根据校区id获取课表下拉
     *
     * @param campusId 校区id
     * @return CampusScheduleVO
     */
    public List<CampusScheduleVO> scheduleByCampusId(String campusId, String courseId) {
        List<CampusScheduleVO> vos = new ArrayList<>();
        this.courseScheduleMapper.selectList(new LambdaQueryWrapper<CourseSchedule>()
                .eq(CourseSchedule::getDeleted, false)
                .eq(ParamUtils.isNotEmpty(campusId), CourseSchedule::getCampusId, campusId)
                .eq(ParamUtils.isNotEmpty(courseId), CourseSchedule::getCourseId, courseId))
                .forEach(courseSchedule -> {
                    CampusScheduleVO vo = new CampusScheduleVO();
                    vo.setScheduleId(courseSchedule.getId());
                    vo.setScheduleName(courseSchedule.getScheduleName());
                    vos.add(vo);
                });
        return vos;
    }
}
